/**************************************************************************
 * @doc PROTOCOL
 * @module PROTOCOL.C | Source code for the Protocol Engine
 *
 * Product     :   SoftPay 2000 (Global Payment Methodology).
 * <nl>Developer   :   SoftPay Development Team (I_SP2000).
 * <nl>Notes       :
 *
 * @head3 PROTOCOL Functions |
 * @index | PROTOCOL
 * @end
 *
 * Copyright (c) 1996-2000 by VeriFone Inc. All rights reserved.
 *
 * No part of this software may be used, stored, compiled, reproduced,
 * modified, transcribed, translated, transmitted, or transferred, in any form
 * or by any means whether electronic, mechanical, magnetic, optical,
 * or otherwise, without the express prior written permission of VeriFone, Inc.
 *
 * Revision History
 * ------------------------------------------------------------------------
 $Log: /SoftPay/SPK2SIM/Source/Protocol/protocol.c $
 *
 ********************************************************************************/
#include <project.h>

#ifdef PET_DEFINED  

#define PROTOCOL_C

#include <stdio.h>
#include <ascii.h>
#include <acldev.h>
#include <aclascii.h>
#include <aclconio.h>
#include <portdef.h>
#include <string.h>
#include <actutil.h>

#include <project.h>

/** /#define DEBUG_PROTOCOL*/
#ifdef DEBUG_PROTOCOL
#define DEBUG_95
#endif

#include <define.h>
#include <msgfuncs.h>
#include <msg.h>
#include <confio.h>
#include <hdt.h>
#include <funcids.h>
#include <table.h>
#include <modem.h>
#include <packet.h>
#include <protomsg.h>
#include <protocol.h>
#include <protfunc.h>
#include <trt.h>
#include <retry.h>
#include <ui.h>
#include <spdebug.h>

/*********************************************************************************************
* @func int | inInitializePacket |
*       Uses the packet type from the HDT table to retrieve the packet initialization
*       function from the function table flexi record and runs the function
*
* @parm PACKET_OBJECT * |pobPacket |
*       Packet object
*
* @end
**********************************************************************************************/

typedef int (*PFI_PO)(PACKET_OBJECT*);

int inInitializePacket(PACKET_OBJECT* pobPacket) {
    PFI_PO pvdFunction;

    if ((pvdFunction = inGetFunctionPointer(shGetPacketType())) == NULL_PFI)
        return(VS_ERR);
    (pvdFunction) (pobPacket);
    return(VS_SUCCESS);
}

/*********************************************************************************************
* @func int | inPushState |
*       Saves the state that the protocol engine executes after
*       subroutine completes.
*
* @parm PROTOCOL_OBJECT * |pobProtocol |
*       Protocol object
* @rdesc return values.
* @flag int |  VS_ERR or VS_SUCCESS
* @end
**********************************************************************************************/

int inPushState(PROTOCOL_OBJECT* pobProtocol) {
    if ((pobProtocol->shReturnIndex + 2) >= MAX_STATES)
        return(VS_ERR);

    pobProtocol->shStates[pobProtocol->shReturnIndex++] = pobProtocol->shIndex;
    pobProtocol->shStates[pobProtocol->shReturnIndex++] = pobProtocol->shLoopBackIndex;
    return(VS_SUCCESS);
}

/*********************************************************************************************
* @func int | inPopState |
*       Restores the state to execute on completion of the subroutine.
*
* @parm PROTOCOL_OBJECT * |pobProtocol |
*       Protocol object
* @rdesc return values.
* @flag int |  VS_ERR or VS_SUCCESS
* @end
**********************************************************************************************/

int inPopState(PROTOCOL_OBJECT* pobProtocol) {
    if (!pobProtocol->shReturnIndex)
        return(VS_ERR);

    pobProtocol->shLoopBackIndex = pobProtocol->shStates[--pobProtocol->shReturnIndex];
    pobProtocol->shIndex = pobProtocol->shStates[--pobProtocol->shReturnIndex];
    return(VS_SUCCESS);
}

/*********************************************************************************************
* @func int | inExecuteState |
*       Runs the function in the current state and sets the next state
*
* @parm PROTOCOL_OBJECT * |pobProtocol |
*       Protocol object
*
* @parm PACKET_OBJECT * |pobPacket |
*       Packet object
*
* @rdesc return values.
* @flag int |  VS_ERR or VS_SUCCESS
* @end
**********************************************************************************************/

typedef int (*PFI_PRO_PKO)(PROTOCOL_OBJECT*,PACKET_OBJECT*);

int inExecuteState(PROTOCOL_OBJECT* pobProtocol,PACKET_OBJECT* pobPacket) {
    PFI_PRO_PKO pinFunction;

    /** /pdebug(("--inExecuteState--%x",pobProtocol->srPET.shStateFunc));*/
    if ((pinFunction = inGetFunctionPointer(pobProtocol->srPET.shStateFunc)) != NULL_PFI) {
        if (((pinFunction) (pobProtocol, pobPacket)) != VS_SUCCESS)
            return(VS_ERR);

        if (pobProtocol->fError)
            pobProtocol->shGoingToState = pobProtocol->srPET.shErrorState;
        else
            pobProtocol->shGoingToState = pobProtocol->srPET.shNextState;
    } else
        pobProtocol->shGoingToState = 0;

    pobProtocol->fError = VS_FALSE;

    return(VS_SUCCESS);
}

/*********************************************************************************************
* @func int | inGotoNextState |
*       Sets the state to execute.
*
* @parm PROTOCOL_OBJECT * |pobProtocol |
*       Protocol object
* @rdesc return values.
* @flag int |  VS_ERR or VS_SUCCESS
* @end
**********************************************************************************************/

int inGotoNextState(PROTOCOL_OBJECT* pobProtocol) {
    int inCurrentIndex;
    char szErrMsg[MESSAGE_SIZE];

    if (!pobProtocol->shGoingToState) {
        pobProtocol->shIndex++;
        if (inLoadConfRec(pobProtocol->szFileName, SIZE_PET_REC, pobProtocol->shIndex, (char *) &pobProtocol->srPET) != VS_SUCCESS) {
            vdGetMessageFromThisFile(PROTOCOL_MSG_FILE, PET_ERR_MSG, szErrMsg);
            vdDisplayErrorString(szErrMsg);
            return(VS_ERR);
        }
        return(VS_SUCCESS);
    }

    /*Save the current position in the protocol file*/
    inCurrentIndex = pobProtocol->shIndex;

    do {
        pobProtocol->shIndex++;

        /* -----------------10/23/96 2:26PM---------------LUIS_R1---
              If pobProtocol->shIndex is equal to inCurrentIndex then we have run through
              all of the records in this block of the protocol table and we have not found
              the protocol "state" that we are looking for. In this case, we are in an
              endless loop so we return with an error.
             --------------------------------------------------*/
        if ((inCurrentIndex == pobProtocol->shIndex) || (inLoadConfRec(pobProtocol->szFileName, SIZE_PET_REC, pobProtocol->shIndex, (char *) &pobProtocol->srPET) != VS_SUCCESS)) {
            vdGetMessageFromThisFile(PROTOCOL_MSG_FILE, PET_ERR_MSG, szErrMsg);
            vdDisplayErrorString(szErrMsg);
            return(VS_ERROR);
        }

        if (pobProtocol->fInSubroutine && (pobProtocol->srPET.shStateFunc == END_SUBROUTINE))
            pobProtocol->shIndex = pobProtocol->shLoopBackIndex - 1;

        if (!pobProtocol->shReturnIndex && (pobProtocol->srPET.shStateFunc == END_PROTOCOL))
            pobProtocol->shIndex = pobProtocol->shLoopBackIndex - 1;
    } while (pobProtocol->srPET.shState != pobProtocol->shGoingToState);

    /*If we have just jumped to a subroutine, we need to set the new loopback value*/
    if (pobProtocol->shReturnIndex && !pobProtocol->fInSubroutine) {
        pobProtocol->shLoopBackIndex = pobProtocol->shIndex;
        pobProtocol->fInSubroutine = VS_TRUE;
    }
    return(VS_SUCCESS);
}

/*********************************************************************************************
* @func int | inGotoFirstState |
*       Sets the first state to execute.
*
* @parm PROTOCOL_OBJECT * |pobProtocol |
*       Protocol object
* @rdesc return values.
* @flag int |  VS_ERR or VS_SUCCESS
* @end
**********************************************************************************************/

int inGotoFirstState(PROTOCOL_OBJECT* pobProtocol) {
    char szErrMsg[MESSAGE_SIZE];

    do {
        if (inLoadConfRec(pobProtocol->szFileName, SIZE_PET_REC, pobProtocol->shIndex, (char *) &pobProtocol->srPET) != VS_SUCCESS) {
            vdGetMessageFromThisFile(PROTOCOL_MSG_FILE, PET_ERR_MSG, szErrMsg);
            vdDisplayErrorString(szErrMsg);
            return(VS_ERROR);
        }
        pobProtocol->shIndex++;
    } while (pobProtocol->srPET.shState != pobProtocol->shGoingToState);

    pobProtocol->shIndex--; /* reverse the last increment */

    /* Make sure if we have to loop we start at where we started */
    pobProtocol->shLoopBackIndex = pobProtocol->shIndex;

    return(VS_SUCCESS);
}


/*********************************************************************************************
* @func int | inProcessProtocol |
*       Loads each record from the protocol table, executes the function in the
*       record and increments to the next state to be executed
*
* @parm PROTOCOL_OBJECT * |pobProtocol |
*       Protocol object
*
* @parm PACKET_OBJECT * |pobPacket |
*       Packet object
*
* @rdesc return values.
* @flag int |  VS_ERR or VS_SUCCESS
* @end
**********************************************************************************************/

int inProcessProtocol(PROTOCOL_OBJECT* pobProtocol,PACKET_OBJECT* pobPacket) {
    /** /pdebug(("--inProcessProtocol--"));*/

    pobProtocol->shIndex = 0;
    pobProtocol->shLoopBackIndex = 1;
    pobProtocol->shReturnIndex = 0;
    pobProtocol->fError = VS_FALSE;
    pobProtocol->fEnd = VS_FALSE;
    pobProtocol->fGetNewLoopBack = VS_FALSE;

    /* The pobProtocol->shGoingToState is set to either SETTLE_STATE or TRAN_STATE */

    vdDisplayMessageFromFile(PROCESSING_MSG21, STATUS_MSG);
    if (inGotoFirstState(pobProtocol))
        return(VS_ERROR);
    if (inExecuteState(pobProtocol, pobPacket))
        return(VS_ERROR);

    do {
        if (inGotoNextState(pobProtocol))
            return(VS_ERROR);
        if (inExecuteState(pobProtocol, pobPacket))
            return(VS_ERROR);
    } while (!pobProtocol->fEnd);

    if (pobProtocol->fError)
        return(VS_ERROR);

    return(VS_SUCCESS);
}

/*********************************************************************************************
* @func void | vdSetupProtocol |
*       Clears the protocol object and gets the protocol table filename
*
* @parm PROTOCOL_OBJECT * |pobProtocol |
*       Protocol object
* @end
**********************************************************************************************/

void vdSetupProtocol(PROTOCOL_OBJECT* pobProtocol) {
    memset((char *) pobProtocol, 0, sizeof(PROTOCOL_OBJECT));
    strcpy(pobProtocol->szFileName, szGetPETFileName());
}

/*********************************************************************************************
* @func int | inSubroutine |
*     Save the current state of protocol engine before jumping to the subroutine
*
* @parm PROTOCOL_OBJECT * |pobProtocol |
*       Protocol object
*
* @parm PACKET_OBJECT * |pobPacket |
*       Packet object
*
* @rdesc return values.
* @flag int |  VS_ERR or VS_SUCCESS
* @end
**********************************************************************************************/

int inSubroutine(PROTOCOL_OBJECT* pobProtocol,PACKET_OBJECT* pobPacket) {
    // pdebug(("--inSub--"));

    if (inPushState(pobProtocol) != VS_SUCCESS)
        return(VS_ERROR);

    pobProtocol->srPET.shNextState = pobProtocol->srPET.shParameter;
    pobProtocol->fInSubroutine = VS_FALSE;
    return(VS_SUCCESS);
}

/*********************************************************************************************
* @func int | inEndSubroutine |
*     Restores and reloads the last state prior to the subroutine
*
* @parm PROTOCOL_OBJECT * |pobProtocol |
*       Protocol object
*
* @parm PACKET_OBJECT * |pobPacket |
*       Packet object
*
* @rdesc return values.
* @flag int |  VS_ERR or VS_SUCCESS
* @end
**********************************************************************************************/

int inEndSubroutine(PROTOCOL_OBJECT* pobProtocol,PACKET_OBJECT* pobPacket) {
    char szErrMsg[MESSAGE_SIZE];

    // pdebug(("--inEndSub--"));
    if (!pobProtocol->fInSubroutine)
        return(VS_SUCCESS);

    if (inPopState(pobProtocol) != VS_SUCCESS)
        return(VS_ERROR);

    if (!pobProtocol->shReturnIndex)
        pobProtocol->fInSubroutine = VS_FALSE;

    if (inLoadConfRec(pobProtocol->szFileName, SIZE_PET_REC, pobProtocol->shIndex, (char *) &pobProtocol->srPET) != VS_SUCCESS) {
        vdGetMessageFromThisFile(PROTOCOL_MSG_FILE, PET_ERR_MSG, szErrMsg);
        vdDisplayErrorString(szErrMsg);
        return(VS_ERROR);
    }

    return(VS_SUCCESS);
}

/*********************************************************************************************
* @func int | inEnd |
*     Sets the end flag in the protocol structure
*
* @parm PROTOCOL_OBJECT * |pobProtocol |
*       Protocol object
*
* @parm PACKET_OBJECT * |pobPacket |
*       Packet object
*
* @rdesc return values.
* @flag int |  VS_ERR or VS_SUCCESS
* @end
**********************************************************************************************/

int inEnd(PROTOCOL_OBJECT* pobProtocol,PACKET_OBJECT* pobPacket) {
    // pdebug(("--inEnd--"));

    pobProtocol->fEnd = VS_TRUE;
    return(VS_SUCCESS);
}

/*********************************************************************************************
* @func int | inErrorProtocol |
*     Returns VS_ERR to terminate protocol processing
*
* @parm PROTOCOL_OBJECT * |pobProtocol |
*       Protocol object
*
* @parm PACKET_OBJECT * |pobPacket |
*       Packet object
*
* @rdesc return values.
* @flag int |  VS_ERR 
* @end
**********************************************************************************************/

int inErrorProtocol(PROTOCOL_OBJECT* pobProtocol,PACKET_OBJECT* pobPacket) {
    // pdebug(("--inError--"));

    pobProtocol->fError = VS_TRUE;
    return(VS_ERR);
}

/********************************************************************************************* 
* @func int | inPackUnpack | 
*       
*
* @parm TRANSACTION_OBJECT *| pobTran | 
*       Transaction object
* 
* @parm short| shInitialState | 
*       Transaction object
* 
* @rdesc return values.
* @flag int |  VS_SUCCESS, VS_ERR
* @end
**********************************************************************************************/

int inPackUnpack(TRANSACTION_OBJECT* pobTran,short shInitialState) {
    PROTOCOL_OBJECT obProtocol;
    PACKET_OBJECT obPacket;

    /** /pdebug(("--inPackUnpack--"));*/


    /* Save pobTran so that retry can occur if error */
    if (shInitialState == TRAN_STATE)
        if (inSaveRetryRequest(pobTran) == VS_ERR)
            return(VS_ERR);

    /*     mb1
     *     Initialize Packet Object with Packet Object Init Function.
     */
    /** /pdebug (("shPacketType(%d)",srHDTRec.shPacketType));*/

    if (inInitializePacket(&obPacket) != VS_SUCCESS)
        return(VS_ERR);
    /*
     *      Run Packet Object -> INIT Function
     */

    /* Set the Original Transaction and initialize the "floating" transaction to the original */
    obPacket.OriginalTransaction = (void *) pobTran;
    obPacket.Transaction = obPacket.OriginalTransaction;

    if (obPacket.inInitialize(&obPacket) != VS_SUCCESS) {
        /** /pdebug(("PUP-Init"));*/
        return(VS_ERR);
    }

    /* Clear the protocol object and gets the protocol table filename */

    vdSetupProtocol(&obProtocol);

    /*
     *      Check for Transaction Cancellation before proceeding.
     */
    if (act_kbd_pending_test(KEY_CANCEL)) {
        vdSetTranError(TXN_CANCELLED);
        if ((!fGetOffline(pobTran)) || (shInitialState == SETTLE_STATE))
            if (obCommunication.inEnd)
                obCommunication.inEnd(&obCommunication);
        obPacket.inDeinitialize(&obPacket);
        /** /pdebug(("PUP-KEY_CANCEL"));*/
        return(VS_ERR);
    }

    /* Run protocol engine */
    obProtocol.shGoingToState = shInitialState;
    if (inProcessProtocol(&obProtocol, &obPacket)) {
        if ((lnGGErrorMessage() == 0L && strlen(pobTran->szRespMsg) == 0) && (shInitialState != SETTLE_STATE))
        {
        	//To make Comm Err more descriptive.....Renu		
              //vdSetTranError(TXMT_FAIL_ERR_MSG);
              vdSetTranError(TXMT_FAIL_ERR_0002_MSG);
		ACTIVITY_LOG("Comm Err : 0002");
        }

        if ((!fGetOffline(pobTran)) || (shInitialState == SETTLE_STATE))
            if (obCommunication.inEnd)
                obCommunication.inEnd(&obCommunication);
        obPacket.inDeinitialize(&obPacket);
        return(VS_ERR);
    }

    /*
     *      Moved Hangup here from BuildAndSend...
     */
    if ((!fGetOffline(pobTran)) || (shInitialState == SETTLE_STATE))
        if (obCommunication.inEnd)
            obCommunication.inEnd(&obCommunication);

    /*
     *      Run Packet Object -> DEINIT Function
     */
    obPacket.inDeinitialize(&obPacket);

    return(VS_SUCCESS);
}

/*********************************************************************************************
* @func int | inPackUnpackWithRetry |
*           Communication protocol function for transactions which checks the retry flag and
*           and retransmitts the transaction if the flag is set.
*
* @parm TRANSACTION_OBJECT *| pobTran | 
*       Transaction object
*
* @rdesc return values.
* @flag int |  VS_SUCCESS
* @end
**********************************************************************************************/

int inPackUnpackWithRetry(TRANSACTION_OBJECT* pobTran) {
    int inStatus;

    //  BT_TRACE (' ', "Begin Communication");
    while (VS_TRUE) {
        if (inPackUnpack(pobTran, TRAN_STATE)) {
            //          BT_TRACE (' ', "End Communication");
            if (inRetryTransaction(pobTran) == VS_SUCCESS)
                continue;
            inStatus = VS_ERR;
        } else {
            //          BT_TRACE (' ', "End Communication");
            inStatus = VS_SUCCESS;
        }
        break;
    }

    vdCancelPendingRetry();
    return (inStatus);
}


/*********************************************************************************************
* @func int | inPackUnpackNoRetry |
*           Communication protocol function for transactions which does not retry
*
* @parm TRANSACTION_OBJECT *| pobTran | 
*       Transaction object
*
* @rdesc return values.
* @flag int |  VS_SUCCESS
* @end
**********************************************************************************************/

int inPackUnpackNoRetry(TRANSACTION_OBJECT* pobTran) {
    int inStatus = VS_SUCCESS;

    //  BT_TRACE (' ', "Begin Communication");

    if (inPackUnpack(pobTran, TRAN_STATE))
        inStatus = VS_ERR;

    //      BT_TRACE (' ', "End Communication");

    return (inStatus);
}


/*********************************************************************************************
* @func int | inPackUnpackForSettlement |
*       Communication protocol function for settle.
*
* @parm TRANSACTION_OBJECT *| pobTran | 
*       Transaction object
*
* @rdesc return values.
* @flag int |  VS_SUCCESS
* @end
**********************************************************************************************/

int inPackUnpackForSettlement(TRANSACTION_OBJECT* pobTran) {
    int inStatus = VS_SUCCESS;

    //  BT_TRACE (' ', "Begin Communication");

    if (inPackUnpack(pobTran, SETTLE_STATE))
        inStatus = VS_ERR;

    //     BT_TRACE (' ', "End Communication");

    return (inStatus);
}

#undef PROTOCOL_C

#endif //PET_DEFINED    
